package com.itheima.auth.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.itheima.auth.config.WechatOAuth2Properties;
import com.itheima.auth.mapper.AdminMapper;
import com.itheima.auth.mapper.OAuthBindingMapper;
import com.itheima.auth.service.WechatOAuth2Service;
import com.itheima.model.admin.dtos.WechatBindDto;
import com.itheima.model.admin.pojos.Admin;
import com.itheima.model.admin.pojos.OAuthBinding;
import com.itheima.model.admin.vos.AdminInfoVo;
import com.itheima.model.admin.vos.WechatAuthVo;
import com.itheima.model.common.dtos.ResponseResult;
import com.itheima.model.common.enums.AppHttpCodeEnum;
import com.itheima.utils.common.JwtUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import com.itheima.common.redis.CacheService;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 微信OAuth2服务实现
 */
@Slf4j
@Service
public class WechatOAuth2ServiceImpl implements WechatOAuth2Service {
    
    @Autowired
    private WechatOAuth2Properties wechatProperties;
    
    @Autowired
    private OAuthBindingMapper oauthBindingMapper;
    
    @Autowired
    private AdminMapper adminMapper;
    
    @Autowired
    private PasswordEncoder passwordEncoder;
    
    @Autowired
    private CacheService cacheService;
    
    @Autowired
    private RestTemplate restTemplate;
    
    private static final String TEMP_TOKEN_PREFIX = "auth:wechat:temp:";
    private static final String STATE_PREFIX = "auth:wechat:state:";
    
    @Override
    public ResponseResult generateAuthUrl(String state) {
        try {
            // 如果没有传state，生成一个
            if (StringUtils.isBlank(state)) {
                state = UUID.randomUUID().toString().replace("-", "");
            }
            
            // 将state存入Redis，5分钟有效
            cacheService.set(STATE_PREFIX + state, "1");
            cacheService.expire(STATE_PREFIX + state, 5, TimeUnit.MINUTES);
            
            // 构建授权URL
            String redirectUri = URLEncoder.encode(wechatProperties.getRedirectUri(), "UTF-8");
            String authUrl = String.format("%s?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=%s#wechat_redirect",
                    wechatProperties.getAuthorizeUrl(),
                    wechatProperties.getAppId(),
                    redirectUri,
                    wechatProperties.getScope(),
                    state);
            
            Map<String, String> result = new HashMap<>();
            result.put("authUrl", authUrl);
            result.put("state", state);
            
            log.info("生成微信扫码登录URL成功");
            return ResponseResult.okResult(result);
        } catch (Exception e) {
            log.error("生成微信扫码登录URL失败", e);
            return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR, "生成授权链接失败");
        }
    }
    
    @Override
    @Transactional
    public ResponseResult handleCallback(String code, String state) {
        // 1. 验证state
        if (StringUtils.isBlank(state) || !cacheService.exists(STATE_PREFIX + state)) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID, "无效的state参数");
        }
        
        // 删除state
        cacheService.delete(STATE_PREFIX + state);
        
        // 2. 通过code获取access_token
        String accessTokenUrl = String.format("%s?appid=%s&secret=%s&code=%s&grant_type=authorization_code",
                wechatProperties.getAccessTokenUrl(),
                wechatProperties.getAppId(),
                wechatProperties.getAppSecret(),
                code);
        
        try {
            ResponseEntity<String> tokenResponse = restTemplate.getForEntity(accessTokenUrl, String.class);
            JSONObject tokenJson = JSON.parseObject(tokenResponse.getBody());
            
            if (tokenJson.containsKey("errcode")) {
                log.error("获取access_token失败：{}", tokenJson.getString("errmsg"));
                return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR, "微信授权失败");
            }
            
            String accessToken = tokenJson.getString("access_token");
            String openid = tokenJson.getString("openid");
            
            // 3. 通过access_token获取用户信息
            String userInfoUrl = String.format("%s?access_token=%s&openid=%s",
                    wechatProperties.getUserInfoUrl(),
                    accessToken,
                    openid);
            
            ResponseEntity<String> userInfoResponse = restTemplate.getForEntity(userInfoUrl, String.class);
            JSONObject userInfoJson = JSON.parseObject(userInfoResponse.getBody());
            
            if (userInfoJson.containsKey("errcode")) {
                log.error("获取用户信息失败：{}", userInfoJson.getString("errmsg"));
                return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR, "获取用户信息失败");
            }
            
            String nickname = userInfoJson.getString("nickname");
            String headimgurl = userInfoJson.getString("headimgurl");
            String unionid = userInfoJson.getString("unionid");
            
            // 4. 查询是否已绑定账号
            LambdaQueryWrapper<OAuthBinding> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(OAuthBinding::getProvider, "wechat")
                   .eq(OAuthBinding::getOpenId, openid);
            
            OAuthBinding binding = oauthBindingMapper.selectOne(wrapper);
            
            // 5. 如果已绑定，直接登录
            if (binding != null) {
                Admin admin = adminMapper.selectById(binding.getAdminId());
                if (admin == null) {
                    return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "绑定的账号不存在");
                }
                if (admin.getStatus() != 1) {
                    return ResponseResult.errorResult(AppHttpCodeEnum.ACCOUNT_DISABLED);
                }
                
                // 查询角色和权限
                List<String> roles = adminMapper.selectRolesByAdminId(admin.getId());
                List<String> permissions = adminMapper.selectPermissionsByAdminId(admin.getId());
                
                // 生成JWT token
                String token = JwtUtil.getAdminToken(
                        admin.getId(),
                        admin.getUsername(),
                        admin.getNickname(),
                        roles,
                        permissions
                );
                
                AdminInfoVo adminInfo = AdminInfoVo.builder()
                        .id(admin.getId())
                        .username(admin.getUsername())
                        .nickname(admin.getNickname())
                        .avatar(admin.getAvatar())
                        .email(admin.getEmail())
                        .phone(admin.getPhone())
                        .roles(roles)
                        .permissions(permissions)
                        .build();
                
                WechatAuthVo authVo = WechatAuthVo.builder()
                        .bound(true)
                        .token(token)
                        .adminInfo(adminInfo)
                        .expiresIn(3600L)
                        .build();
                
                log.info("微信扫码登录成功：{}", admin.getUsername());
                return ResponseResult.okResult(authVo);
            }
            
            // 6. 如果未绑定，生成临时token
            String tempToken = UUID.randomUUID().toString().replace("-", "");
            
            // 将微信用户信息存入Redis，10分钟有效
            Map<String, String> wechatUserInfo = new HashMap<>();
            wechatUserInfo.put("openid", openid);
            wechatUserInfo.put("unionid", unionid != null ? unionid : "");
            wechatUserInfo.put("nickname", nickname);
            wechatUserInfo.put("headimgurl", headimgurl);
            
            cacheService.set(TEMP_TOKEN_PREFIX + tempToken, JSON.toJSONString(wechatUserInfo));
            cacheService.expire(TEMP_TOKEN_PREFIX + tempToken, 10, TimeUnit.MINUTES);
            
            WechatAuthVo authVo = WechatAuthVo.builder()
                    .bound(false)
                    .tempToken(tempToken)
                    .wechatNickname(nickname)
                    .wechatAvatar(headimgurl)
                    .build();
            
            log.info("微信用户未绑定账号，返回临时token");
            return ResponseResult.okResult(authVo);
            
        } catch (Exception e) {
            log.error("处理微信回调失败", e);
            return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR, "微信授权处理失败");
        }
    }
    
    @Override
    @Transactional
    public ResponseResult bindWechat(WechatBindDto dto) {
        // 1. 验证参数
        if (dto == null || StringUtils.isBlank(dto.getTempToken()) 
                || StringUtils.isBlank(dto.getUsername()) 
                || StringUtils.isBlank(dto.getPassword())) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID, "参数不完整");
        }
        
        // 2. 从Redis获取微信用户信息
        String wechatInfoStr = cacheService.get(TEMP_TOKEN_PREFIX + dto.getTempToken());
        if (StringUtils.isBlank(wechatInfoStr)) {
            return ResponseResult.errorResult(AppHttpCodeEnum.TOKEN_EXPIRE, "临时token已过期，请重新扫码");
        }
        
        JSONObject wechatInfo = JSON.parseObject(wechatInfoStr);
        String openid = wechatInfo.getString("openid");
        String unionid = wechatInfo.getString("unionid");
        
        // 3. 验证管理员账号密码
        Admin admin = adminMapper.selectByUsername(dto.getUsername());
        if (admin == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "用户名或密码错误");
        }
        
        if (!passwordEncoder.matches(dto.getPassword(), admin.getPassword())) {
            return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_PASSWORD_ERROR, "用户名或密码错误");
        }
        
        if (admin.getStatus() != 1) {
            return ResponseResult.errorResult(AppHttpCodeEnum.ACCOUNT_DISABLED);
        }
        
        // 4. 检查该管理员是否已绑定微信
        LambdaQueryWrapper<OAuthBinding> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(OAuthBinding::getAdminId, admin.getId())
               .eq(OAuthBinding::getProvider, "wechat");
        
        OAuthBinding existBinding = oauthBindingMapper.selectOne(wrapper);
        if (existBinding != null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_EXIST, "该账号已绑定其他微信");
        }
        
        // 5. 检查该微信是否已绑定其他账号
        LambdaQueryWrapper<OAuthBinding> wrapper2 = new LambdaQueryWrapper<>();
        wrapper2.eq(OAuthBinding::getProvider, "wechat")
                .eq(OAuthBinding::getOpenId, openid);
        
        OAuthBinding existBinding2 = oauthBindingMapper.selectOne(wrapper2);
        if (existBinding2 != null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_EXIST, "该微信已绑定其他账号");
        }
        
        // 6. 创建绑定关系
        OAuthBinding binding = new OAuthBinding();
        binding.setAdminId(admin.getId());
        binding.setProvider("wechat");
        binding.setOpenId(openid);
        binding.setUnionId(unionid);
        binding.setCreatedTime(LocalDateTime.now());
        
        oauthBindingMapper.insert(binding);
        
        // 7. 删除临时token
        cacheService.delete(TEMP_TOKEN_PREFIX + dto.getTempToken());
        
        // 8. 查询角色和权限
        List<String> roles = adminMapper.selectRolesByAdminId(admin.getId());
        List<String> permissions = adminMapper.selectPermissionsByAdminId(admin.getId());
        
        // 9. 生成JWT token
        String token = JwtUtil.getAdminToken(
                admin.getId(),
                admin.getUsername(),
                admin.getNickname(),
                roles,
                permissions
        );
        
        AdminInfoVo adminInfo = AdminInfoVo.builder()
                .id(admin.getId())
                .username(admin.getUsername())
                .nickname(admin.getNickname())
                .avatar(admin.getAvatar())
                .email(admin.getEmail())
                .phone(admin.getPhone())
                .roles(roles)
                .permissions(permissions)
                .build();
        
        WechatAuthVo authVo = WechatAuthVo.builder()
                .bound(true)
                .token(token)
                .adminInfo(adminInfo)
                .expiresIn(3600L)
                .build();
        
        log.info("绑定微信账号成功：{}", admin.getUsername());
        return ResponseResult.okResult(authVo);
    }
    
    @Override
    @Transactional
    public ResponseResult unbindWechat(Integer adminId) {
        if (adminId == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        
        LambdaQueryWrapper<OAuthBinding> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(OAuthBinding::getAdminId, adminId)
               .eq(OAuthBinding::getProvider, "wechat");
        
        int count = oauthBindingMapper.delete(wrapper);
        if (count > 0) {
            log.info("解绑微信账号成功，管理员ID：{}", adminId);
            return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
        } else {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "未绑定微信账号");
        }
    }
}

